﻿namespace MicroCloud.Systems
{
    /// <summary>
    /// 文件相关扩展
    /// </summary>
    public static class Extensions
    {
        /// <summary>
        /// 从 <see cref="IConfiguration"/> 创建 <see cref="FileOptions"/>
        /// </summary>
        /// <param name="configuration"></param>
        /// <returns></returns>
        public static FileOption GetFileOption(this IConfiguration configuration)
        {
            var fileOption = configuration.GetOptions<FileOption>("MicroCloud:File", (conf, key) =>
            {
                IConfigurationSection section = configuration.GetSection(key);
                if (!section.Exists())
                {
                    throw new Exception($"文件配置选项不存在，请配置 {key} 节点");
                }

                var opts = section.Get<FileOption>();
                //资源存放根路径
                if (!Path.IsPathRooted(opts.Root))
                {
                    //如果是相对路径
                    opts.Root = Path.GetFullPath(opts.Root, DirectoryHelper.RootPath());
                }

                if (!opts.RequestPath.StartsWith('/'))
                {
                    //设置请求路径
                    opts.RequestPath = $"/{opts.RequestPath}";
                }
                //todo:
                //if (opts.Mappings != null && opts.Mappings.Length > 0)
                //{
                //    var repeated = opts.Mappings.GroupBy(m => m.FileType).FirstOrDefault(m => m.Count() > 1);
                //    if (repeated != null)
                //    {
                //        throw new MicroCloudException($"静态文件流映射中重复类型：{repeated.First().FileType}");
                //    }
                //}
                return opts;
            });

            return fileOption;
        }

        /// <summary>
        /// 文件Dto(上传|更新)验证
        /// </summary>
        public static OperationResult CheckOption(this UploadInputDto dto, FileOption fileOption)
        {
            Check.NotNull(dto, nameof(dto));
            Check.NotNull(fileOption, nameof(FileOption));
            OperationResult operation = OperationResult.Success();

            //文件大小验证
            var fileSize = dto.FileStream.Length;
            var limitSize = fileOption.SizeLimit; 
            if (limitSize > 0 && fileSize > limitSize)
            {
                return OperationResult.Error($"文件 {dto.FileName} 大小 {fileSize} 超出系统限制 {limitSize}");
            }

            //文件类型验证
            dto.FileExtension = dto.FileExtension.Trim();
            dto.FileExtension = (dto.FileExtension.StartsWith('.') ? dto.FileExtension[1..] : dto.FileExtension).ToUpper();
            if (fileOption.AllowedExts != null && fileOption.AllowedExts.Length > 0)
            {
                var isAllow = fileOption.AllowedExts.Any(o => (o.Trim().StartsWith('.') ? o.Trim()[1..] : o.Trim()).ToUpper().Equals(dto.FileExtension));
                if (!isAllow)
                {
                    return OperationResult.Error($"文件 {dto.FileName} 类型 {dto.FileExtension} 不允许上传");
                }
            }

            if (fileOption.DisallowedExts != null && fileOption.DisallowedExts.Length > 0)
            {
                var isDisallowed = fileOption.DisallowedExts.Any(o => (o.Trim().StartsWith('.') ? o.Trim()[1..] : o.Trim()).ToUpper().Equals(dto.FileExtension));
                if (isDisallowed)
                {
                    return OperationResult.Error($"文件 {dto.FileName} 类型 {dto.FileExtension} 不允许上传");
                }
            }                     
            return operation;
        }

        /// <summary>
        /// 获取 文件配置分组路由信息
        /// </summary>
        /// <param name="fileOption"></param>
        /// <returns></returns>
        public static string GetRouteOption(this FileOption fileOption)
        {
            string optionRoute = string.Empty;
            var dateNow = DateTime.Now;
            var dateNowYear = dateNow.Year.ToString().PadLeft(4, '0');
            var dateNowMonth = dateNow.Month.ToString().PadLeft(2, '0');
            var dateNowDay = dateNow.Day.ToString().PadLeft(2, '0');

            switch (fileOption.StoreGroup)
            {
                case FileStoreGroup.NoGroup:
                    optionRoute = string.Empty;
                    break;
                case FileStoreGroup.ByYear:
                    optionRoute = $"/{dateNowYear}";
                    break;
                case FileStoreGroup.ByYearMonth:
                    optionRoute = $"/{dateNowYear}/{dateNowMonth}";
                    break;
                case FileStoreGroup.ByYearMonthDay:
                    optionRoute = $"/{dateNowYear}/{dateNowMonth}/{dateNowDay}";
                    break;
            }
            return optionRoute;
        }

        /// <summary>
        /// 获取文件网络路径
        /// </summary>
        /// <param name="fileOption">文件配置信息</param>
        /// <param name="serializedName">文件名称</param>
        /// <returns></returns>
        public static string GetRequestPath(this FileOption fileOption, string serializedName)
        {
            var routes = new List<string>();
            var routeOption = fileOption.GetRouteOption();
            if (!routeOption.IsNullOrWhiteSpace()) routes.Add(routeOption);

            routes.Add(serializedName);
            
            string requestPath = string.Join('/', routes);
            if (!requestPath.StartsWith('/'))
            {
                requestPath = $"/{requestPath}";
            }
            return requestPath;
        }

        /// <summary>
        /// 获取文件物理路径
        /// </summary>
        /// <param name="fileOption">文件配置信息</param>
        /// <param name="serializedName">文件名称</param>
        /// <returns></returns>
        public static string GetPhysicalPath(this FileOption fileOption, string serializedName = null)
        {
            var routeOption = fileOption.GetRouteOption();
            return Path.Combine(routeOption.Replace('/', '\\'), serializedName ?? string.Empty);
        }

        /// <summary>
        /// 获取文件物理路径
        /// </summary>
        /// <param name="fileOption">文件配置信息</param>
        /// <param name="serializedName">文件名称</param>
        /// <returns></returns>
        public static string GetPhysicalFullPath(this FileOption fileOption, string serializedName = null)
        {
            var physicalPath = fileOption.GetPhysicalPath(serializedName);
            return Path.Combine(fileOption.Root, physicalPath.TrimStart(new char[] { '\\'}));
        }
    }
}
